//! # 加密工具
//!
//! 提供常用的加密、解密、哈希和签名功能。

use crate::core::{SecurityError, SecurityResult};
use std::collections::HashMap;
use base64::engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD};
use base64::Engine;

/// 加密工具类
pub struct CryptoUtils {
    algorithms: HashMap<String, String>,
}

impl CryptoUtils {
    /// 创建新的加密工具实例
    pub fn new() -> Self {
        let mut algorithms = HashMap::new();
        algorithms.insert("default_hash".to_string(), "SHA256".to_string());
        algorithms.insert("default_cipher".to_string(), "AES-256-GCM".to_string());
        
        Self { algorithms }
    }

    /// 计算字符串的SHA256哈希
    pub fn sha256(&self, input: &str) -> SecurityResult<String> {
        use sha2::{Sha256, Digest};
        
        let mut hasher = Sha256::new();
        hasher.update(input.as_bytes());
        let result = hasher.finalize();
        Ok(hex::encode(result))
    }

    /// 计算字符串的SHA512哈希
    pub fn sha512(&self, input: &str) -> SecurityResult<String> {
        use sha2::{Sha512, Digest};
        
        let mut hasher = Sha512::new();
        hasher.update(input.as_bytes());
        let result = hasher.finalize();
        Ok(hex::encode(result))
    }

    /// 计算HMAC-SHA256
    pub fn hmac_sha256(&self, key: &[u8], message: &str) -> SecurityResult<String> {
        use hmac::{Hmac, Mac};
        use sha2::Sha256;
        
        type HmacSha256 = Hmac<Sha256>;
        
        let mut mac = HmacSha256::new_from_slice(key)
            .map_err(|e| SecurityError::KeyManagementError(e.to_string()))?;
        
        mac.update(message.as_bytes());
        let result = mac.finalize();
        Ok(hex::encode(result.into_bytes()))
    }

    /// 生成随机字节
    pub fn generate_random_bytes(&self, length: usize) -> SecurityResult<Vec<u8>> {
        use rand::RngCore;
        
        let mut rng = rand::thread_rng();
        let mut bytes = vec![0u8; length];
        rng.fill_bytes(&mut bytes);
        Ok(bytes)
    }

    /// 生成随机字符串（Base64编码）
    pub fn generate_random_string(&self, length: usize) -> SecurityResult<String> {
        let bytes = self.generate_random_bytes(length)?;
        Ok(STANDARD.encode(bytes))
    }

    /// 生成密码哈希（使用bcrypt）
    pub fn hash_password(&self, password: &str) -> SecurityResult<String> {
        bcrypt::hash(password, bcrypt::DEFAULT_COST)
            .map_err(|e| SecurityError::HashComputationFailed(e.to_string()))
    }

    /// 验证密码
    pub fn verify_password(&self, password: &str, hash: &str) -> SecurityResult<bool> {
        bcrypt::verify(password, hash)
            .map_err(|e| SecurityError::HashComputationFailed(e.to_string()))
    }

    /// 生成安全的随机令牌
    pub fn generate_secure_token(&self, length: usize) -> SecurityResult<String> {
        use rand::Rng;
        
        const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        let mut rng = rand::thread_rng();
        
        let token: String = (0..length)
            .map(|_| {
                let idx = rng.gen_range(0..CHARSET.len());
                CHARSET[idx] as char
            })
            .collect();
        
        Ok(token)
    }

    /// 计算时间常数比较（防止时间攻击）
    pub fn constant_time_compare(&self, a: &[u8], b: &[u8]) -> bool {
        use subtle::ConstantTimeEq;
        a.ct_eq(b).into()
    }

    /// Base64编码
    pub fn base64_encode(&self, input: &[u8]) -> String {
        STANDARD.encode(input)
    }

    /// Base64解码
    pub fn base64_decode(&self, input: &str) -> SecurityResult<Vec<u8>> {
        STANDARD.decode(input)
            .map_err(|e| SecurityError::DecodingError(e.to_string()))
    }

    /// URL安全的Base64编码
    pub fn base64_url_encode(&self, input: &[u8]) -> String {
        URL_SAFE_NO_PAD.encode(input)
    }

    /// URL安全的Base64解码
    pub fn base64_url_decode(&self, input: &str) -> SecurityResult<Vec<u8>> {
        URL_SAFE_NO_PAD.decode(input)
            .map_err(|e| SecurityError::DecodingError(e.to_string()))
    }

    /// 生成UUID
    pub fn generate_uuid(&self) -> String {
        uuid::Uuid::new_v4().to_string()
    }

    /// 计算CRC32校验和
    pub fn crc32(&self, input: &[u8]) -> u32 {
        crc32fast::hash(input)
    }

    /// 生成安全的会话ID
    pub fn generate_session_id(&self) -> SecurityResult<String> {
        let random_bytes = self.generate_random_bytes(32)?;
        let timestamp = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .map_err(|e| SecurityError::TimestampValidationFailed(e.to_string()))?
            .as_secs();
        
        let session_data = format!("{}-{}", timestamp, hex::encode(random_bytes));
        self.sha256(&session_data)
    }

    /// 验证数据完整性
    pub fn verify_integrity(&self, data: &[u8], expected_hash: &str) -> SecurityResult<bool> {
        let actual_hash = self.sha256(&String::from_utf8_lossy(data))?;
        Ok(self.constant_time_compare(actual_hash.as_bytes(), expected_hash.as_bytes()))
    }

    /// 生成CSRF令牌
    pub fn generate_csrf_token(&self) -> SecurityResult<String> {
        self.generate_secure_token(32)
    }

    /// 掩码敏感数据（用于日志记录）
    pub fn mask_sensitive_data(&self, data: &str, mask_char: char, visible_chars: usize) -> String {
        if data.len() <= visible_chars * 2 {
            mask_char.to_string().repeat(data.len().min(8))
        } else {
            let start = &data[..visible_chars];
            let end = &data[data.len() - visible_chars..];
            let middle = mask_char.to_string().repeat(data.len() - visible_chars * 2);
            format!("{}{}{}", start, middle, end)
        }
    }

    /// 验证哈希强度
    pub fn validate_hash_strength(&self, hash: &str, min_length: usize) -> bool {
        hash.len() >= min_length && 
        hash.chars().any(|c| c.is_ascii_uppercase()) &&
        hash.chars().any(|c| c.is_ascii_lowercase()) &&
        hash.chars().any(|c| c.is_ascii_digit())
    }
}

impl Default for CryptoUtils {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_crypto_utils_creation() {
        let crypto = CryptoUtils::new();
        assert!(!crypto.algorithms.is_empty());
    }

    #[test]
    fn test_hash_functions() {
        let crypto = CryptoUtils::new();
        
        let input = "hello world";
        let sha256_hash = crypto.sha256(input).unwrap();
        let sha512_hash = crypto.sha512(input).unwrap();
        
        assert_eq!(sha256_hash.len(), 64); // SHA256 is 32 bytes = 64 hex chars
        assert_eq!(sha512_hash.len(), 128); // SHA512 is 64 bytes = 128 hex chars
        
        // Test consistency
        assert_eq!(crypto.sha256(input).unwrap(), sha256_hash);
        assert_eq!(crypto.sha512(input).unwrap(), sha512_hash);
    }

    #[test]
    fn test_hmac() {
        let crypto = CryptoUtils::new();
        let key = b"secret_key";
        let message = "test message";
        
        let hmac = crypto.hmac_sha256(key, message).unwrap();
        assert_eq!(hmac.len(), 64); // HMAC-SHA256 is 32 bytes = 64 hex chars
        
        // Test consistency
        assert_eq!(crypto.hmac_sha256(key, message).unwrap(), hmac);
    }

    #[test]
    fn test_random_generation() {
        let crypto = CryptoUtils::new();
        
        let bytes1 = crypto.generate_random_bytes(16).unwrap();
        let bytes2 = crypto.generate_random_bytes(16).unwrap();
        
        assert_eq!(bytes1.len(), 16);
        assert_eq!(bytes2.len(), 16);
        assert_ne!(bytes1, bytes2); // Should be different
        
        let string1 = crypto.generate_random_string(16).unwrap();
        let string2 = crypto.generate_random_string(16).unwrap();
        
        assert_ne!(string1, string2); // Should be different
    }

    #[test]
    fn test_password_hashing() {
        let crypto = CryptoUtils::new();
        let password = "test_password_123";
        
        let hash = crypto.hash_password(password).unwrap();
        assert!(hash.len() > 50); // bcrypt hashes are typically 60 chars
        
        // Verify password
        assert!(crypto.verify_password(password, &hash).unwrap());
        assert!(!crypto.verify_password("wrong_password", &hash).unwrap());
    }

    #[test]
    fn test_base64_encoding() {
        let crypto = CryptoUtils::new();
        let data = b"hello world";
        
        let encoded = crypto.base64_encode(data);
        let decoded = crypto.base64_decode(&encoded).unwrap();
        
        assert_eq!(decoded, data);
        
        // Test URL-safe encoding
        let url_encoded = crypto.base64_url_encode(data);
        let url_decoded = crypto.base64_url_decode(&url_encoded).unwrap();
        
        assert_eq!(url_decoded, data);
    }

    #[test]
    fn test_constant_time_compare() {
        let crypto = CryptoUtils::new();
        
        let data1 = b"same_data";
        let data2 = b"same_data";
        let data3 = b"different";
        
        assert!(crypto.constant_time_compare(data1, data2));
        assert!(!crypto.constant_time_compare(data1, data3));
    }

    #[test]
    fn test_secure_token_generation() {
        let crypto = CryptoUtils::new();
        
        let token1 = crypto.generate_secure_token(32).unwrap();
        let token2 = crypto.generate_secure_token(32).unwrap();
        
        assert_eq!(token1.len(), 32);
        assert_eq!(token2.len(), 32);
        assert_ne!(token1, token2);
        
        // Check that token contains only valid characters
        assert!(token1.chars().all(|c| c.is_ascii_alphanumeric()));
    }

    #[test]
    fn test_uuid_generation() {
        let crypto = CryptoUtils::new();
        
        let uuid1 = crypto.generate_uuid();
        let uuid2 = crypto.generate_uuid();
        
        assert_ne!(uuid1, uuid2);
        assert_eq!(uuid1.len(), 36); // Standard UUID format
        assert!(uuid1.contains('-'));
    }

    #[test]
    fn test_session_id_generation() {
        let crypto = CryptoUtils::new();
        
        let session1 = crypto.generate_session_id().unwrap();
        let session2 = crypto.generate_session_id().unwrap();
        
        assert_ne!(session1, session2);
        assert_eq!(session1.len(), 64); // SHA256 hash length
    }

    #[test]
    fn test_data_masking() {
        let crypto = CryptoUtils::new();
        
        let sensitive_data = "1234567890123456";
        let masked = crypto.mask_sensitive_data(sensitive_data, '*', 4);
        
        assert!(masked.starts_with("1234"));
        assert!(masked.ends_with("3456"));
        assert!(masked.contains('*'));
        
        // Test short data
        let short_data = "123";
        let masked_short = crypto.mask_sensitive_data(short_data, '*', 4);
        assert_eq!(masked_short, "***");
    }

    #[test]
    fn test_integrity_verification() {
        let crypto = CryptoUtils::new();
        
        let data = b"test data";
        let hash = crypto.sha256(&String::from_utf8_lossy(data)).unwrap();
        
        assert!(crypto.verify_integrity(data, &hash).unwrap());
        
        let corrupted_data = b"corrupted data";
        assert!(!crypto.verify_integrity(corrupted_data, &hash).unwrap());
    }

    #[test]
    fn test_crc32() {
        let crypto = CryptoUtils::new();
        
        let data = b"test data";
        let crc1 = crypto.crc32(data);
        let crc2 = crypto.crc32(data);
        
        assert_eq!(crc1, crc2); // Should be consistent
        
        let different_data = b"different data";
        let crc3 = crypto.crc32(different_data);
        
        assert_ne!(crc1, crc3); // Should be different
    }
}